
package src;

import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.jws.WebMethod;
import javax.jws.WebService;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.SchemaFactory;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.xmlgraphics.util.MimeConstants;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
/**
 * Classe permettant le traitement sur le serveur de l'ensemble des fonctions.
 * @author David & Valentin.
 */
@Stateless
@LocalBean
@WebService
public class DocumentService  {
    
    /**
     * Permet de sauvegarder le dernier identifiant du document XML.
     */
    public static Integer dernierIdentifiant = 0;
    
    /**
     * Permet le dépot de document xml sur le serveur, avec une validation par rapport au schéma
     * @param tableau tableau représentent le fichier à déposer
     * @return l'identifiant du fichier qui a été déposé
     * @throws ParserConfigurationException
     * @throws SAXException
     * @throws IOException
     */
    @WebMethod
    public synchronized int depotDocument(byte[] tableau) throws ParserConfigurationException, SAXException, IOException{
        try{
            //Validation du document xml avec le parser SAX 
            SAXParserFactory factory = SAXParserFactory.newInstance();
            factory.setValidating(false);
            factory.setNamespaceAware(true);
            
            SchemaFactory schemaFactory =
                    SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
            InputStream xsd = DocumentService.class.getResourceAsStream("schema.xsd");
            factory.setSchema(schemaFactory.newSchema(new Source[] {new StreamSource(xsd)}));
            
            SAXParser parser = factory.newSAXParser();
            
            XMLReader reader = parser.getXMLReader();
            SimpleErrorHandler simpleHandler = new SimpleErrorHandler();
            
            
            reader.setErrorHandler(simpleHandler);
            ByteArrayInputStream bis = new ByteArrayInputStream(tableau);
            reader.parse(new InputSource(bis));
            //création du document sur le serveur, si le document xml a été validé, le document a comme nom son id.
            dernierIdentifiant++;
            File newFile = new File(dernierIdentifiant.toString()+".xml");
            try (FileOutputStream fos = new FileOutputStream(newFile)) {
                fos.write(tableau);
                
            }
        }catch(SAXParseException e){
            throw e;
        }
        return dernierIdentifiant;
    }
    
    /**
     * Permet de rechercher un document XML sur le serveur.
     * @param listeMotCle liste des mots clés recherchés
     * @param typeRecherche type de recherche "et" ou "ou"
     * @return les identifiants des docuement avec leurs titre, sous la forme identifiant:titre;identifiant:titre;
     * @throws IOException
     */
    @WebMethod
    public String rechercheDocument(List<String> listeMotCle, String typeRecherche) throws IOException{
        
        File directoryToScan = new File(".");
        File[] files = directoryToScan.listFiles();
        String result = "";
        //Pour chaque fichier xml, on  vérifie si les mots clés recherchés sont présents dans le document
        for(int i = 0; i < files.length; i++){
            try {
                XMLInputFactory inputFactory = XMLInputFactory.newInstance();
                InputStream input = new FileInputStream(files[i]);
                XMLStreamReader xmlStreamReader = inputFactory.createXMLStreamReader(input);
                System.out.println(files[i].getName());
                
                Boolean trouve = false;
                String titreTrouve = "";
                //on recherche chaque mot clé dans le document
                for(String motRecherche : listeMotCle){
                    while (xmlStreamReader.hasNext()) {
                        int event = xmlStreamReader.next();
                        
                        if (event == XMLStreamConstants.START_ELEMENT && xmlStreamReader.getLocalName().equals("titre")) {
                            titreTrouve = xmlStreamReader.getElementText();
                        }
                        //Si l'on est dans une balsie  <motcle> on vérifie le mot 
                        if (event == XMLStreamConstants.START_ELEMENT && xmlStreamReader.getLocalName().equals("motcle")) {
                            try {
                                String text = xmlStreamReader.getElementText();
                                if(typeRecherche.equalsIgnoreCase("et"))trouve = false;
                                if(motRecherche.equalsIgnoreCase(text)){
                                    trouve = true;
                                    
                                    System.out.println(motRecherche + "=="+ text );
                                    break;
                                }
                                
                            } catch (XMLStreamException e) {
                                
                            }
                        }
                    }
                }
                if(trouve){
                    result += files[i].getName().split(".xml")[0]+":"+titreTrouve+";";
                }
            } catch (XMLStreamException ex) {
                Logger.getLogger(DocumentService.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return result;
    }
    
    /**
     * Permet de retourner un document XML à partir de son identifiant.
     * @param idDocument l'identifiant du document
     * @return le document trouvé sous la forme d'un tableau de byte
     * @throws IOException Permet de
     */
    @WebMethod
    public byte[] retourneDocument(int idDocument) throws IOException{
        //recherche du document avec son identifiant
        File directoryToScan = new File(".");
        File[] files = directoryToScan.listFiles();
        for(int i = 0; i<files.length; i++){
            String nomDocument = files[i].getName().split(".xml")[0];
            //transformation du docuement trouvé en un tableau de byte[]
            if(nomDocument.equals(idDocument+"")){
                try {
                    byte[] bFile = new byte[(int) files[i].length()];
                    FileInputStream fileInputStream = new FileInputStream(files[i]);
                    fileInputStream.read(bFile);
                    fileInputStream.close();
                    return bFile;
                } catch (FileNotFoundException ex) {
                    Logger.getLogger(DocumentService.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
        return new byte[0];
    }
    
    /**
     * Permet la génération du PDF à partir d'un XML et d'une feuille XSL.
     * @param idDocument l'identifiant du document
     * @param xslFo tableau de byte représentant le tableau de document
     * @return un tableau de byte représentant le document xsl-fo
     * @throws FOPException
     */
    @WebMethod
    public byte[] generePDF(int idDocument, byte[] xslFo) {
        File directoryToScan = new File(".");
        File[] files = directoryToScan.listFiles();
        //recherche du docuement
        for(int i = 0; i<files.length; i++){
            String nomDocument = files[i].getName().split(".xml")[0];
            if(nomDocument.equals(idDocument+"")){
               FopFactory fopFactory = FopFactory.newInstance();
               OutputStream out;
                try{
                    //document de sortie
                    long name = new Date().getTime();
                    out = new BufferedOutputStream(new FileOutputStream(new File(name+".pdf")));
                    Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);
                    TransformerFactory factory = TransformerFactory.newInstance();
                    
                    ByteArrayInputStream bis = new ByteArrayInputStream(xslFo);
                    
                    Source xslt = new StreamSource(bis);
                    Transformer transformer = factory.newTransformer(xslt);
                    Source src = new StreamSource(files[i]);
                    Result res = new SAXResult(fop.getDefaultHandler());
                    //trasformation du docuement xsl en pdf
                    transformer.transform(src, res);
                    out.close();
                    File file = new File(name+".pdf");
                    byte[] pdfinByte = Utils.Commons.convertirFile2Bytes(file);
                    return pdfinByte;
                }catch(FileNotFoundException e){
                }catch (TransformerConfigurationException ex) {
                    Logger.getLogger(DocumentService.class.getName()).log(Level.SEVERE, null, ex);
                } catch (IOException ex) {
                    Logger.getLogger(DocumentService.class.getName()).log(Level.SEVERE, null, ex);
                } catch (TransformerException ex) {
                    Logger.getLogger(DocumentService.class.getName()).log(Level.SEVERE, null, ex);
                } catch (FOPException ex) {
                    Logger.getLogger(DocumentService.class.getName()).log(Level.SEVERE, null, ex);
                }
                
            }
        }
        byte[] r = new byte[0];
        return  r;
    }
    
    /**
     * Permet de reprendre l'état du serveur lors de son arret, notamment le dernier identifiant du document
     */
    @PostConstruct
    private void demarrage() {
        try {
            // ouverture d'un flux d'entrée depuis le fichier "personne.serial"
            FileInputStream fis = new FileInputStream("id.serial");
            
            // création d'un "flux objet" avec le flux fichier
            ObjectInputStream ois= new ObjectInputStream(fis);
            try {
                // désérialisation : lecture de l'objet depuis le flux d'entrée
                dernierIdentifiant = (Integer) ois.readObject();
                
            } finally {
                // on ferme les flux
                try {
                    ois.close();
                } finally {
                    fis.close();
                }
            }
        } catch( IOException | ClassNotFoundException ioe) {
        }
    }
    
    /**
     * Permet de sauvegarder l'état du serveur lors de son arret, notamment le dernier identifiant du document
     */
    @PreDestroy
    private void arret() {
        try{
            File f = new File("id.serial");
            // ouverture d'un flux de sortie vers le fichier "personne.serial"
            FileOutputStream fos = new FileOutputStream("id.serial");
            // création d'un "flux objet" avec le flux fichier
            ObjectOutputStream oos= new ObjectOutputStream(fos);
            oos.writeObject(dernierIdentifiant);
            // on vide le tampon
            oos.flush();
        }catch(IOException e){
            
        }
    }
}
